package dominia.net;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;

import org.apache.log4j.Logger;


public class Connection {
	private InetSocketAddress	addr;
	private Socket			sock;
	private Logger			logger;
	private InputStream		in;
	private OutputStream		out;

	public Connection()
	{
		this.logger = Logger.getLogger(this.getClass());

		this.sock = new Socket();
	}

	public Connection(InetAddress addr, int port)
	{
		this();
		this.addr = new InetSocketAddress(addr, port);
	}

	public Connection(InetSocketAddress addr)
	{
		this();
		this.addr = addr;
	}

	/**
	 * 
	 * @param addr
	 */
	public void setRemoteAddress(InetSocketAddress addr)
	{
		this.addr = addr;
	}

	/**
	 * 
	 */
	public void setRemoteAddress(InetAddress addr, int port)
	{
		this.addr = new InetSocketAddress(addr, port);
	}

	/**
	 * 
	 * @param addr a string representation of the remote IP address
	 * @param port the remote port
	 */
	public void setRemoteAddress(String addr, int port)
	{
		this.addr = new InetSocketAddress(addr, port);
	}

	/**
	 * Returns the socket address of the remote endpoint of the connection.
	 *
	 * @return
	 */
	public SocketAddress getRemoteAddress()
	{
		return this.sock.getRemoteSocketAddress();
	}

	public void connect()
	  throws ConnectException
	{
		if (this.addr == null)
			throw new ConnectException("No remote address specified");

		logger.info("Connecting to server at " + addr);
		try {
			sock.connect(addr);
		} catch (IOException e) {
			logger.error("Connection failed: " + e.getMessage());
			throw new ConnectException(e.getMessage());
		}
	}

	public void disconnect()
	{
		logger.info("Disconnecting from server at " + addr);
		if (!sock.isConnected()) {
			logger.warn("Socket is already disconnected");
			return;
		}

		try {
			sock.close();
		} catch (IOException e) {
			logger.error("Disconnection error: " + e.getMessage());
		}
	}

	public int send(byte[] src, int len)
	    throws IOException
	{
		int sent = 0;

		if (out == null) {
			try {
				this.out = this.sock.getOutputStream();
			} catch (IOException e) {
				logger.error("Failed to get socket output stream: " +
				    e.getMessage());
				throw e;
			}
		}

		try {
			this.out.write(src, 0, len);
		} catch (IOException e) {
			logger.error("Failed to send: " + e.getMessage());
			throw e;
		}

		return sent;
	}

	public int recv(byte[] dst, int len)
	    throws IOException
	{
		int recvd = 0;

		if (this.in == null) {
			try {
				this.in = this.sock.getInputStream();
			} catch (IOException e) {
				logger.error("Failed to get socket input stream: " +
				    e.getMessage());
				throw e;
			}
		}

		try {
			recvd = this.in.read(dst, 0, len);
		} catch (IOException e) {
			logger.error("Failed to receive: " + e.getMessage());
			throw e;
		}

		return recvd;
	}
}